﻿using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Buffers;
using System.IO.Pipelines;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using IOP.Pulsar.Abstractions;
using IOP.Net;
using Microsoft.Extensions.Options;
using System.Linq;
using System.Net.NetworkInformation;
using IOP.Pulsar.Options;

namespace IOP.Pulsar.Server
{
    /// <summary>
    /// TCP服务器
    /// </summary>
    public class TCPServer<TContext> : IHostedService
        where TContext : class
    {
        private const int MinimumBufferSize = 1500;
        /// <summary>
        /// 日志
        /// </summary>
        private readonly ILogger<TCPServer<TContext>> _looger;
        /// <summary>
        /// 配置
        /// </summary>
        private readonly IConfiguration _configuration;
        /// <summary>
        /// 生命周期函数
        /// </summary>
        private readonly IHostLifetime _applicationLifetime;
        /// <summary>
        /// 服务
        /// </summary>
        private readonly IServiceProvider _service;
        /// <summary>
        /// 配置
        /// </summary>
        private readonly IOptions<TCPServerOption> _options;
        /// <summary>
        /// Socket池
        /// </summary>
        private readonly ISocketPool _socketPool;
        /// <summary>
        /// 服务器名
        /// </summary>
        private string _serverName { get; set; }

        /// <summary>
        /// 构造函数
        /// </summary>
        public TCPServer(ILogger<TCPServer<TContext>> logger, 
            IConfiguration configuration, 
            IHostLifetime applicationLifetime,
            IServiceProvider service,
            ISocketPool socketpool,
            IOptions<TCPServerOption> options)
        {
            _looger = logger;
            _configuration = configuration;
            _applicationLifetime = applicationLifetime;
            _service = service;
            _options = options;
            _socketPool = socketpool;
        }

        /// <summary>
        /// 启动
        /// </summary>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public Task StartAsync(CancellationToken cancellationToken)
        {
            //_applicationLifetime.ApplicationStarted.Register(() => _looger.LogInformation($"TCPServer is started"));
            //_applicationLifetime.ApplicationStopped.Register(() => _looger.LogInformation("TCPServer is stopped"));
            //_applicationLifetime.ApplicationStopping.Register(() => _looger.LogInformation("TCPServer is stopping"));
            return Task.Run(async () =>
            {
                try
                {
                    var name = _options.Value.ServerName;
                    if (string.IsNullOrEmpty(name)) throw new Exception($"No name in this server, server is stopping");
                    _serverName = name;
                    int.TryParse(_configuration[$"{name}:Port"], out int port);
                    int.TryParse(_configuration[$"{name}:ConnectionCount"], out int number);
                    if (port == 0)
                    {
                        if (_options.Value.Port == 0)
                            throw new Exception($"Load Port failed, please check your appsettings.json, server is stopping. server name: {name}");
                        else port = _options.Value.Port;
                    }
                    if (number == 0) throw new Exception($"Load ConnectionCount failed, please check your appsettings.json, server is stopping. server name: {name}");

                    IPAddress ip = null;
                    if (string.IsNullOrEmpty(_options.Value.Address))
                    {
                        var hostName = Dns.GetHostName();
                        var ips = await Dns.GetHostAddressesAsync(hostName);
                        if (!ips.Any()) throw new ArgumentException($"Cannot get ip address from {hostName}, server is stopping, server name: {hostName}");
                        ip = ips.Where(x => x.AddressFamily == AddressFamily.InterNetwork).First();
                    }
                    else
                    {
                        var ips = await Dns.GetHostAddressesAsync(_options.Value.Address);
                        ips = ips.Where(x => x.AddressFamily == AddressFamily.InterNetwork).ToArray();
                        if (!ips.Any()) throw new ArgumentException($"Cannot get ip address from {_options.Value.Address}, server is stopping, server name: {name}");
                        ip = ips[0];
                    }

                    var listenSocket = new Socket(SocketType.Stream, ProtocolType.Tcp);
                    listenSocket.Bind(new IPEndPoint(ip, port));
                    listenSocket.Listen(number);
                    var mainSocket = new SocketMonitor(listenSocket);
                    _looger.LogInformation($"{name} : TCPServer is started running in {ip.ToString()}:{port}");

                    while (true)
                    {
                        var socket = await listenSocket.AcceptAsync();
                        var newSocket = new SocketMonitor(socket);
                        _looger.LogInformation($"{_serverName} : [{newSocket.Identifier}]: connected");
                        ProcessAsync(newSocket);
                    }
                }
                catch (Exception e)
                {
                    _looger.LogError(e, "error");
                }
                finally
                {
                    await StopAsync(cancellationToken);
                }
            }, cancellationToken);
        }

        /// <summary>
        /// 停止
        /// </summary>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public Task StopAsync(CancellationToken cancellationToken)
        {
            return Task.Run(() =>
            {
                Console.Read();
            }, cancellationToken);
        }

        /// <summary>
        /// 关闭应用程序
        /// </summary>
        public async void CloseServer()
        {
            var cancel = new CancellationTokenSource();
            await _applicationLifetime.StopAsync(cancel.Token);
        }

        /// <summary>
        /// 处理函数
        /// </summary>
        /// <param name="socket"></param>
        /// <returns></returns>
        private void ProcessAsync(SocketMonitor socket)
        {
            try
            {
                var pipe = new Pipe();
                _ = FillPipeAsync(socket, pipe.Writer);
                _ = ReadPipeAsync(socket, pipe.Reader);
            }
            catch (Exception e)
            {
                _looger.LogError(e.Message + e.Source);
            }
            finally
            {
                socket.Dispose();
            }
        }

        /// <summary>
        /// 管线填充函数
        /// </summary>
        /// <param name="socket"></param>
        /// <param name="writer"></param>
        /// <returns></returns>
        private async ValueTask FillPipeAsync(SocketMonitor socket, PipeWriter writer)
        {
            while (true)
            {
                try
                {
                    Memory<byte> memory = writer.GetMemory(MinimumBufferSize);
                    int bytesRead = await socket.ConnectedSocket.ReceiveAsync(memory, SocketFlags.None);
                    if (bytesRead == 0) break;
                    // Tell the PipeWriter how much was read
                    writer.Advance(bytesRead);
                }
                catch(Exception e)
                {
                    _looger.LogError($"{_serverName} : {e.Message} \n {e.StackTrace}");
                    break;
                }
                // Make the data available to the PipeReader
                FlushResult result = await writer.FlushAsync();
                if (result.IsCompleted) break;

            }
            // Signal to the reader that we're done writing
            writer.Complete();
        }

        /// <summary>
        /// 读取管线
        /// </summary>
        /// <param name="socket"></param>
        /// <param name="reader"></param>
        /// <returns></returns>
        private async ValueTask ReadPipeAsync(SocketMonitor socket, PipeReader reader)
        {
            var entry = _service.GetRequiredService<IProductLineEntry<TContext>>();
            try
            {
                while (true)
                {
                    ReadResult result = await reader.ReadAsync();
                    socket.UpdateLastMessageTime(DateTime.Now);
                    ReadOnlySequence<byte> buffer = result.Buffer;
                    entry.EntryHandle(ref buffer, socket);
                    // We sliced the buffer until no more data could be processed
                    // Tell the PipeReader how much we consumed and how much we left to process
                    reader.AdvanceTo(buffer.Start, buffer.End);
                    if (result.IsCompleted) break;
                }
            }
            catch (Exception e)
            {
                _looger.LogError($"{_serverName} : {e.Message} \n {e.StackTrace}");
            }
            finally
            {
                _looger.LogInformation($"{_serverName} : [{socket.Identifier}]: disconnected");
                entry.Dispose();
                reader.Complete();
            }
        }
    }
}
